API Connect / Webex Contact Center

API Connect - Webex Contact Center

Guia del conector ss_bi_wxcc para autenticar contra Webex, ejecutar consultas GraphQL de BI y recorrer paginas de tareas de forma reutilizable. El modulo cubre unicamente la autenticacion OAuth y el endpoint /search; para el resto de APIs, SDKs y webhooks se debe consultar la documentacion oficial.

Mapa del modulo

  • auth: configuracion OAuth, refresh token y cache del access token.
  • client: cliente HTTP, busqueda GraphQL y paginado completo.
  • epoch_ms: conversion de fechas timezone-aware a epoch en milisegundos.

Flujo recomendado

  • Validar variables de entorno sin imprimir secretos.
  • Construir el cliente con WebexAuthenticator.from_env().
  • Ejecutar fetch_all_tasks(...) y materializar con Spark.
Uso previsto

Para que sirve

ss_bi_wxcc encapsula la autenticacion OAuth de Webex Contact Center y el paginado de consultas GraphQL del endpoint /search.

Problema que resuelve

Las consultas de tareas de Webex devuelven resultados paginados. El notebook o job no deberia repetir el loop de cursor en cada caso de uso: solo debe definir la ventana, la query y el destino de datos.

  • Centraliza el refresh de tokens.
  • Normaliza errores HTTP y errores GraphQL.
  • Acumula paginas hasta que hasNextPage sea falso.
  • Corta con error si el cursor viene vacio o repetido.
No imprimas WEBEX_CLIENT_SECRET, WEBEX_REFRESH_TOKEN, access tokens ni refresh tokens en notebooks compartidos o salidas de jobs.
Alcance del modulo: autenticacion OAuth, ejecucion contra el endpoint /search y paginado de tareas. No implementa el resto de endpoints REST, webhooks, SDKs ni recursos administrativos de Webex Contact Center.
Fuente oficial

Documentacion de la API

La referencia completa de Webex Contact Center esta publicada en Webex for Developers.

Sitio oficial

Consultar Webex Contact Center | Webex for Developers para revisar guias, referencia de APIs, GraphQL/Search, webhooks, SDKs y cambios publicados por Cisco.

  • La documentacion oficial incluye REST, GraphQL, webhooks y SDKs.
  • Este modulo no reemplaza esa documentacion: solo estandariza autenticacion y uso del endpoint /search.
  • Para nuevas queries o campos disponibles, validar siempre el schema y ejemplos en el portal oficial.
Referencia

Componentes principales

Funciones y clases que se importan normalmente desde el paquete.

auth OAuth

WebexOAuthConfig

WebexOAuthConfig.from_env()

Lee WEBEX_CLIENT_ID y WEBEX_CLIENT_SECRET. Usa por defecto https://webexapis.com/v1/access_token.

auth token store

EnvironmentTokenStore

EnvironmentTokenStore("WEBEX_REFRESH_TOKEN")

Lee el refresh token desde variables de entorno y guarda en memoria del proceso un refresh token rotado si Webex devuelve uno nuevo.

auth access token

WebexAuthenticator

WebexAuthenticator.from_env(token_store=EnvironmentTokenStore())

Obtiene un access token valido. Si el token cacheado vencio, refresca usando el refresh token configurado.

client GraphQL

WebexContactCenterClient.search

client.search(query, variables, org_id=None)

Ejecuta una consulta GraphQL contra {WXCC_API_BASE_URL}/search. Si se informa org_id, lo envia como parametro orgId.

client paginado limite defensivo

WebexContactCenterClient.fetch_all_tasks

client.fetch_all_tasks(query, from_epoch, to_epoch, first_cursor="0", max_pages=100)

Recorre paginas de tareas usando pageInfo.endCursor. La query debe aceptar variables $from, $to y $cursor, y debe pedir pageInfo.hasNextPage y pageInfo.endCursor.

Parametro Uso
query Query GraphQL con el shape esperado por el endpoint de BI.
from_epoch / to_epoch Ventana temporal en epoch milliseconds.
first_cursor Cursor inicial. Para una consulta nueva se usa "0".
max_pages Tope defensivo para evitar loops largos o inconsistentes.
progress / progress_callback Opcional para mostrar avance o integrarlo con logging del job.
Configuracion

Variables requeridas

El modulo espera la configuracion en variables de entorno o en el backend de secretos de la plataforma.

Variable Descripcion
WEBEX_CLIENT_ID Client ID de la integracion OAuth.
WEBEX_CLIENT_SECRET Client secret de la integracion OAuth. Debe tratarse como secreto.
WEBEX_REFRESH_TOKEN Refresh token inicial para pedir access tokens.
WXCC_API_BASE_URL Base URL del API de Webex Contact Center, por ejemplo https://api.wxcc-us1.cisco.com.
En notebooks locales puede usarse load_dotenv(). En Databricks, Fabric, Airflow u otra plataforma, reemplazar esa carga por secretos administrados.
End-to-end

Ejemplo de uso en notebook

La explicacion mantiene la estructura markdown esperada para el notebook: cada bloque corresponde a una seccion ## y el codigo puede copiarse debajo de ese titulo.

Estructura markdown sugerida

  • Cada paso se muestra como una celda markdown renderizada.
  • Debajo de cada celda markdown se incluye la celda de codigo correspondiente.
  • El punto ## 2. Cargar configuracion es solo una validacion de variables requeridas (no requerido para plataformas productivas).

1. Preparacion

Instalar el paquete o agregar el path local, cargar imports y evitar exponer secretos. En plataformas productivas, reemplazar load_dotenv() por el backend de secretos.

from __future__ import annotations

import json
import os
from datetime import UTC, datetime, timedelta

from dotenv import load_dotenv
from ss_bi_wxcc import (
    EnvironmentTokenStore,
    WebexAuthenticator,
    WebexContactCenterClient,
    epoch_ms,
)

2. Cargar configuracion

Esta celda es solo una validacion de variables requeridas. La configuracion real se lee al construir WebexAuthenticator y WebexContactCenterClient.

load_dotenv()

required_env_vars = [
    "WEBEX_CLIENT_ID",
    "WEBEX_CLIENT_SECRET",
    "WEBEX_REFRESH_TOKEN",
    "WXCC_API_BASE_URL",
]
missing = [name for name in required_env_vars if not os.getenv(name)]
if missing:
    raise RuntimeError(f"Faltan variables de entorno requeridas: {', '.join(missing)}")

print(json.dumps({"api_base_url_configured": True}, indent=2))

3. Construir cliente autenticado

WebexAuthenticator obtiene y refresca el access token. El cliente HTTP queda listo para llamar al endpoint /search.

authenticator = WebexAuthenticator.from_env(
    token_store=EnvironmentTokenStore(),
)

client = WebexContactCenterClient.from_env(
    authenticator=authenticator,
    timeout=60,
)

4. Definir ventana, query y cursor

Preparar fechas en epoch milliseconds y una query GraphQL que incluya pageInfo.hasNextPage y pageInfo.endCursor.

now = datetime.now(UTC)
from_epoch = epoch_ms(now - timedelta(days=1))
to_epoch = epoch_ms(now)

task_query = """
query BIContactCenterTasks($from: Long!, $to: Long!, $cursor: String!) {
  task(
    from: $from
    to: $to
    pagination: { cursor: $cursor }
  ) {
    tasks {
      id
      # Agregar aqui los campos habilitados para BI en el schema:
      # channelType
      # createdTime
      # endedTime
      # queueName
      # teamName
      # agentName
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
"""

5. Paginado reutilizable

El loop de cursores vive en WebexContactCenterClient.fetch_all_tasks(...). El notebook solo define la query, la ventana de fechas y el destino de datos.

Paso 6

Ejecutar el paginado y usar Spark

Para volumenes BI, evitar pd.json_normalize(all_tasks). La recomendacion es materializar con Spark y seguir el procesamiento distribuido.

6. Ejecutar el paginado

Traer los registros con fetch_all_tasks(...) y crear un DataFrame Spark. Para volumenes BI, evitar pd.json_normalize(all_tasks).

all_tasks = client.fetch_all_tasks(
    query=task_query,
    from_epoch=from_epoch,
    to_epoch=to_epoch,
    max_pages=25,
    progress=True,
)

if not all_tasks:
    df_all_tasks = spark.createDataFrame([], "id string")
else:
    df_all_tasks = spark.createDataFrame(all_tasks)

display(df_all_tasks.limit(5))
print(f"Total de registros: {df_all_tasks.count()}")
Si las tareas tienen estructuras anidadas heterogeneas, definir un schema explicito de Spark antes de crear el DataFrame. Eso evita inferencias inestables entre ejecuciones.
from pyspark.sql.types import StringType, StructField, StructType

task_schema = StructType([
    StructField("id", StringType(), True),
    StructField("channelType", StringType(), True),
    StructField("createdTime", StringType(), True),
    StructField("endedTime", StringType(), True),
    StructField("queueName", StringType(), True),
    StructField("teamName", StringType(), True),
    StructField("agentName", StringType(), True),
])

df_all_tasks = spark.createDataFrame(all_tasks, schema=task_schema)

(
    df_all_tasks
    .write
    .mode("append")
    .format("delta")
    .saveAsTable("bronze.webex_contact_center_tasks")
)
Operativo

Recomendaciones para produccion

Buenas practicas para llevar el ejemplo del notebook a un job estable.

Ventanas de carga

Procesar ventanas chicas y repetibles. Si el volumen diario es alto, preferir ventanas horarias y reintentos por ventana.

Metadata

Persistir from_epoch, to_epoch, fecha de ejecucion, cantidad de registros y estado del job.

Schema

Mantener schema explicito para evitar cambios por inferencia automatica cuando Webex agrega o no devuelve campos.

Secretos

Guardar client secret y refresh token en un administrador de secretos. Si Webex rota refresh tokens, persistir el nuevo token en ese backend.